package ID3

import (
	"encoding/binary"
	"fmt"

	"strconv"
	"unicode/utf16"
	"unicode/utf8"
)

type Id3v23FrameType string

const (
	FRAME_TYPE_TITLE       = "TIT2"
	FRAME_TYPE_ARTIST      = "TPE1"
	FRAME_TYPE_ALBUM_TITLE = "TALB"
	FRAME_TYPE_TRACK       = "TRCK"
	FRAME_TYPE_YEAR        = "TYER"
	FRAME_TYPE_TRACK_TYPE  = "TCON"
	FRAME_TYPE_COMMENT     = "COMM"
)

type Id3v23Info struct {
	frames map[Id3v23FrameType][]byte
}

type Id3v23Frame struct {
	frameType []byte //4byte frameType
	size      []byte //4byte size
	flag      []byte //2byte
	data      []byte //data
}

func (self *Id3v23Info) init() {
	if self.frames == nil {
		self.frames = make(map[Id3v23FrameType][]byte)
	}
}
func (self *Id3v23Info) Set(frameType Id3v23FrameType, data []byte) {
	self.init()
	self.frames[frameType] = getFrameData(frameType, data)
}

func getFrameData(frameType Id3v23FrameType, data []byte) []byte {
	newData := make([]byte, 0, len(data)+1)
	newData = append(newData, []byte{1, 0xfe, 0xff}...)
	newData = append(newData, toUtf16(data)...)
	data = newData
	ret := make([]byte, 0, len(data)+10)
	ret = append(ret, []byte(frameType)...)
	sizeBuffer := make([]byte, 4)
	//log.Println("ldata:", uint32(len(data)))
	binary.BigEndian.PutUint32(sizeBuffer, uint32(len(data)))
	ret = append(ret, sizeBuffer...)
	ret = append(ret, []byte{0, 0}...)
	ret = append(ret, data...)
	return ret
}

func toUtf16(data []byte) []byte {
	ret := make([]byte, 0)
	buf := make([]byte, 2)
	runeBuffer := make([]rune, 0)
	for len(data) > 0 {

		r, size := utf8.DecodeRune(data)
		data = data[size:]
		runeBuffer = append(runeBuffer, r)
	}
	u16 := utf16.Encode(runeBuffer)
	for _, u := range u16 {
		binary.BigEndian.PutUint16(buf, u)
		ret = append(ret, buf...)
	}
	return ret
}

func (self *Id3v23Info) ToByties() []byte {
	dataBytes := make([]byte, 0)
	for _, v := range self.frames {
		dataBytes = append(dataBytes, v...)
	}
	ret := make([]byte, 0, len(dataBytes)+10)
	//header
	ret = append(ret, []byte("ID3")...)
	//verson
	ret = append(ret, 0x03)
	//revision
	ret = append(ret, 0x00)
	//flag
	ret = append(ret, 0x00)
	//size
	ret = append(ret, getInfoLength(uint32(len(dataBytes)))...)
	//data
	ret = append(ret, dataBytes...)
	return ret
}

func (self *Id3v23Info) getDataLen() uint32 {
	var ret uint32
	for _, v := range self.frames {
		ret += uint32(len(v))
	}
	return ret

}

func getInfoLength(length uint32) []byte {
	ret := make([]byte, 4)
	strs := fmt.Sprintf("%b", length)
	strLen := len(strs)
	sectId := 4
	//log.Println("ll:", len(strs))
	for {
		startIndex := strLen - 7 - 7*(4-sectId)
		endIndex := strLen - 7*(4-sectId)

		//log.Printf("%d:%d", startIndex, endIndex)
		if endIndex <= 0 {
			break
		}
		if startIndex < 0 {
			startIndex = 0
		}
		newSect, _ := strconv.ParseInt(strs[startIndex:endIndex], 2, 8)
		//log.Printf("new s :%s , %d", strs[startIndex:endIndex], len(strs[startIndex:endIndex]))
		ret[sectId-1] = byte(newSect)
		if startIndex == 0 {
			break
		}
		sectId--
	}
	return ret
}

func GetID3V23Length(data []byte) uint32 {
	//log.Printf("data : %X", data)
	if len(data) < 10 {
		return 0
	}
	if string(data[:3]) != "ID3" {
		return 0
	}
	return getID3V23Length(data[6:10]) + 10
}

func getID3V23Length(headSizeByties []byte) uint32 {
	//log.Printf("headSizeByties %x", headSizeByties)
	return uint32(headSizeByties[0])*uint32(0x200000) + uint32(headSizeByties[1])*uint32(0x4000) + uint32(headSizeByties[2])*0x80 + uint32(headSizeByties[3])
}
